Impara Go in 10 minuti
Go (noto anche come Golang) è un linguaggio di programmazione compilato e staticamente tipizzato progettato da Google. È noto per la sua semplicità, efficienza e ottimo supporto per la concorrenza. Questo tutorial ti aiuterà a imparare rapidamente la programmazione in Go.
1. Scrivere il primo programma in Go
Iniziamo con un programma semplice. Crea un file chiamato hello.go
e inserisci il seguente codice:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Salva il file ed esegui il seguente comando nel terminale:
go run hello.go
L’output sarà:
Hello, World!
Questo semplice programma dimostra la struttura di base di Go:
package main
dichiara il nome del pacchettoimport "fmt"
importa il pacchetto di formattazione per operazioni di I/Ofunc main()
è il punto di ingresso del programmafmt.Println()
stampa testo sulla console
2. Sintassi di base
Go ha una sintassi pulita e semplice. A differenza di Python, Go usa parentesi graffe {}
per definire blocchi di codice e richiede punti e virgola alla fine delle istruzioni (anche se di solito vengono inseriti automaticamente).
// Questo è un commento su una riga
fmt.Println("Hello, World!")
/*
Questo è un commento su più righe
che si estende su più linee
*/
Regole di sintassi di base in Go:
- Blocchi di codice: Definiti da parentesi graffe
{}
- Commenti: I commenti su una riga iniziano con
//
, quelli su più righe con/* */
- Istruzioni: Terminano con punti e virgola (inseriti automaticamente)
- Dichiarazione del pacchetto: Ogni file inizia con una dichiarazione del pacchetto
3. Variabili e tipi di dati
Go è staticamente tipizzato, il che significa che devi dichiarare i tipi delle variabili. Tuttavia, Go supporta l’inferenza di tipo con l’operatore :=
.
Metodi di dichiarazione delle variabili:
// Dichiarazione esplicita del tipo
var nome string = "John"
var età int = 25
// Inferenza di tipo
nome := "John"
età := 25
// Dichiarazione multipla di variabili
var x, y int = 10, 20
x, y := 10, 20
Principali tipi di dati di base in Go:
- Tipi interi:
int
,int8
,int16
,int32
,int64
,uint
,uint8
,uint16
,uint32
,uint64
,uintptr
- Tipi float:
float32
,float64
- Stringa:
string
- Booleano:
bool
- Tipi complessi:
complex64
,complex128
3.1 Tipi numerici
Go fornisce vari tipi numerici per diversi casi d’uso:
// Tipi interi
var età int = 25
var numeroPiccolo int8 = 127
var numeroGrande int64 = 9223372036854775807
// Tipi float
var temperatura float32 = 36.5
var pi float64 = 3.14159265359
// Numeri complessi
var numeroComplesso complex64 = 3 + 4i
3.2 Tipo stringa
Le stringhe in Go sono sequenze di byte e sono immutabili:
// Dichiarazione di stringa
var saluto string = "Hello, Go!"
nome := "Alice"
// Operazioni sulle stringhe
fmt.Println(len(saluto)) // Lunghezza della stringa
fmt.Println(saluto[0]) // Accesso al primo carattere (byte)
fmt.Println(saluto[0:5]) // Slicing della stringa
fmt.Println(strings.ToUpper(nome)) // Conversione in maiuscolo
3.3 Tipo booleano
Il tipo booleano ha due valori: true
e false
:
var isAttivo bool = true
var isCompletato bool = false
// Operazioni booleane
risultato1 := true && false // false
risultato2 := true || false // true
risultato3 := !true // false
4. Costanti
Le costanti vengono dichiarate usando la parola chiave const
e non possono essere modificate:
const Pi = 3.14159
const MaxUtenti = 1000
// Costanti multiple
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
// Costanti tipizzate
const Versione string = "1.0.0"
5. Strutture dati
Go fornisce diverse strutture dati integrate per memorizzare e manipolare dati.
5.1 Array
Gli array sono sequenze di dimensione fissa di elementi dello stesso tipo:
// Dichiarazione di array
var numeri [5]int = [5]int{1, 2, 3, 4, 5}
nomi := [3]string{"Alice", "Bob", "Charlie"}
// Accesso agli elementi
fmt.Println(numeri[0]) // 1
numeri[0] = 10 // Modifica elemento
// Lunghezza dell'array
fmt.Println(len(numeri)) // 5
5.2 Slice
Le slice sono array dinamici che possono crescere e ridursi:
// Dichiarazione di slice
numeri := []int{1, 2, 3, 4, 5}
var sliceVuota []int
// Creazione di slice da array
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // [2, 3, 4]
// Operazioni sulle slice
numeri = append(numeri, 6) // Aggiungi elemento
numeri = append(numeri, 7, 8, 9) // Aggiungi più elementi
// Capacità e lunghezza della slice
fmt.Println(len(numeri)) // Lunghezza: 9
fmt.Println(cap(numeri)) // Capacità: 10 (può variare)
5.3 Mappe
Le mappe sono collezioni non ordinate di coppie chiave-valore:
// Dichiarazione di mappa
studente := map[string]interface{}{
"nome": "John",
"età": 20,
"corso": "Informatica",
}
// Dichiarazione alternativa
var punteggi map[string]int = make(map[string]int)
punteggi["matematica"] = 95
punteggi["scienze"] = 88
// Accesso e modifica
fmt.Println(studente["nome"])
studente["età"] = 21
studente["media"] = 3.8
// Accesso sicuro
if telefono, esiste := studente["telefono"]; esiste {
fmt.Println(telefono)
} else {
fmt.Println("Telefono non fornito")
}
// Iterazione sulla mappa
for chiave, valore := range studente {
fmt.Printf("%s: %v\n", chiave, valore)
}
5.4 Struct
Le struct sono collezioni di campi che possono avere tipi diversi:
// Definizione di struct
type Persona struct {
Nome string
Età int
Città string
}
// Creazione di istanze di struct
persona1 := Persona{"Alice", 25, "New York"}
persona2 := Persona{
Nome: "Bob",
Età: 30,
Città: "London",
}
// Accesso ai campi
fmt.Println(persona1.Nome)
persona1.Età = 26
6. Operazioni e operatori
Go fornisce un ricco set di operatori per vari calcoli e confronti.
- Operatori aritmetici:
+
,-
,*
,/
,%
(modulo) - Operatori di confronto:
==
,!=
,>
,<
,>=
,<=
- Operatori logici:
&&
,||
,!
- Operatori bitwise:
&
,|
,^
,<<
,>>
- Operatori di assegnazione:
=
,+=
,-=
,*=
,/=
6.1 Operatori aritmetici
a, b := 10, 3
fmt.Printf("Addizione: %d\n", a + b) // 13
fmt.Printf("Sottrazione: %d\n", a - b) // 7
fmt.Printf("Moltiplicazione: %d\n", a * b) // 30
fmt.Printf("Divisione: %d\n", a / b) // 3
fmt.Printf("Modulo: %d\n", a % b) // 1
6.2 Operatori di confronto
x, y := 5, 10
fmt.Printf("Uguale: %t\n", x == y) // false
fmt.Printf("Non uguale: %t\n", x != y) // true
fmt.Printf("Maggiore di: %t\n", x > y) // false
fmt.Printf("Minore di: %t\n", x < y) // true
6.3 Operatori logici
a, b := true, false
fmt.Printf("Operazione AND: %t\n", a && b) // false
fmt.Printf("Operazione OR: %t\n", a || b) // true
fmt.Printf("Operazione NOT: %t\n", !a) // false
7. Controllo del flusso
Go fornisce diverse istruzioni di controllo del flusso per gestire l’esecuzione del programma.
7.1 Istruzioni if
età := 20
if età >= 18 {
fmt.Println("Adulti")
} else if età >= 13 {
fmt.Println("Adolescenti")
} else {
fmt.Println("Bambini")
}
// if con istruzione breve
if punteggio := 85; punteggio >= 90 {
fmt.Println("Voto: A")
} else if punteggio >= 80 {
fmt.Println("Voto: B")
} else {
fmt.Println("Voto: C")
}
7.2 Cicli for
Go ha un solo costrutto di ciclo: for
// Ciclo for di base
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// Ciclo in stile while
conteggio := 0
for conteggio < 5 {
fmt.Println(conteggio)
conteggio++
}
// Ciclo infinito
for {
fmt.Println("Questo continuerà per sempre")
break // Usa break per uscire
}
// Ciclo range (per slice, array, mappe)
frutti := []string{"mela", "banana", "ciliegia"}
for indice, frutto := range frutti {
fmt.Printf("%d: %s\n", indice, frutto)
}
7.3 Istruzioni switch
giorno := "Lunedì"
switch giorno {
case "Lunedì":
fmt.Println("Inizio della settimana")
case "Venerdì":
fmt.Println("Il weekend è vicino")
case "Sabato", "Domenica":
fmt.Println("Weekend!")
default:
fmt.Println("Giorno normale")
}
// Switch senza espressione
punteggio := 85
switch {
case punteggio >= 90:
fmt.Println("Voto: A")
case punteggio >= 80:
fmt.Println("Voto: B")
case punteggio >= 70:
fmt.Println("Voto: C")
default:
fmt.Println("Voto: F")
}
8. Funzioni
Le funzioni in Go sono cittadini di prima classe e supportano valori di ritorno multipli.
8.1 Funzioni di base
func saluta(nome string) string {
return "Ciao, " + nome + "!"
}
// Chiamata della funzione
messaggio := saluta("John")
fmt.Println(messaggio)
8.2 Valori di ritorno multipli
func dividi(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("non si può dividere per zero")
}
return a / b, nil
}
// Utilizzo di valori di ritorno multipli
risultato, err := dividi(10, 2)
if err != nil {
fmt.Println("Errore:", err)
} else {
fmt.Println("Risultato:", risultato)
}
8.3 Valori di ritorno nominati
func calcolaRettangolo(larghezza, altezza float64) (area float64, perimetro float64) {
area = larghezza * altezza
perimetro = 2 * (larghezza + altezza)
return // ritorno nudo
}
area, perimetro := calcolaRettangolo(5, 3)
fmt.Printf("Area: %.2f, Perimetro: %.2f\n", area, perimetro)
8.4 Funzioni variadiche
func somma(numeri ...int) int {
totale := 0
for _, num := range numeri {
totale += num
}
return totale
}
fmt.Println(somma(1, 2, 3, 4)) // 10
fmt.Println(somma(5, 10, 15)) // 30
9. Puntatori
Go ha i puntatori ma con una sintassi più semplice rispetto a C/C++:
func modificaValore(x *int) {
*x = *x * 2
}
func main() {
valore := 10
fmt.Println("Prima:", valore) // 10
modificaValore(&valore)
fmt.Println("Dopo:", valore) // 20
}
10. Metodi
I metodi sono funzioni con un argomento ricevitore:
type Rettangolo struct {
Larghezza float64
Altezza float64
}
// Metodo con ricevitore per valore
func (r Rettangolo) Area() float64 {
return r.Larghezza * r.Altezza
}
// Metodo con ricevitore per puntatore
func (r *Rettangolo) Scala(fattore float64) {
r.Larghezza *= fattore
r.Altezza *= fattore
}
rett := Rettangolo{Larghezza: 5, Altezza: 3}
fmt.Println("Area:", rett.Area()) // 15
rett.Scala(2)
fmt.Println("Area scalata:", rett.Area()) // 60
11. Interfacce
Le interfacce definiscono firme di metodi che i tipi possono implementare:
type Forma interface {
Area() float64
Perimetro() float64
}
type Cerchio struct {
Raggio float64
}
func (c Cerchio) Area() float64 {
return 3.14159 * c.Raggio * c.Raggio
}
func (c Cerchio) Perimetro() float64 {
return 2 * 3.14159 * c.Raggio
}
func stampaInfoForma(s Forma) {
fmt.Printf("Area: %.2f, Perimetro: %.2f\n", s.Area(), s.Perimetro())
}
cerchio := Cerchio{Raggio: 5}
stampaInfoForma(cerchio)
12. Gestione degli errori
Go utilizza la gestione esplicita degli errori piuttosto che le eccezioni:
func leggiFile(filename string) (string, error) {
data, err := os.ReadFile(filename)
if err != nil {
return "", fmt.Errorf("impossibile leggere il file %s: %w", filename, err)
}
return string(data), nil
}
contenuto, err := leggiFile("esempio.txt")
if err != nil {
fmt.Println("Errore:", err)
return
}
fmt.Println("Contenuto:", contenuto)
13. Concorrenza con Goroutine
Le goroutine sono thread leggeri gestiti dal runtime di Go:
func lavoratore(id int) {
for i := 0; i < 3; i++ {
fmt.Printf("Lavoratore %d: %d\n", id, i)
time.Sleep(time.Millisecond * 100)
}
}
func main() {
// Avvia più goroutine
for i := 1; i <= 3; i++ {
go lavoratore(i)
}
// Attendi che le goroutine completino
time.Sleep(time.Second)
fmt.Println("Tutti i lavoratori hanno completato")
}
14. Canali
I canali vengono utilizzati per la comunicazione tra goroutine:
func produttore(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i // Invia valore al canale
time.Sleep(time.Millisecond * 100)
}
close(ch) // Chiudi il canale quando hai finito
}
func consumatore(ch <-chan int) {
for valore := range ch {
fmt.Println("Ricevuto:", valore)
}
}
func main() {
ch := make(chan int, 3) // Canale bufferizzato
go produttore(ch)
consumatore(ch)
fmt.Println("Comunicazione del canale completata")
}
15. Operazioni sui file
Go fornisce metodi semplici per leggere e scrivere file:
// Lettura di file
data, err := os.ReadFile("esempio.txt")
if err != nil {
fmt.Println("Errore nella lettura del file:", err)
return
}
fmt.Println("Contenuto del file:", string(data))
// Scrittura di file
contenuto := "Ciao, Go!\n"
err = os.WriteFile("output.txt", []byte(contenuto), 0644)
if err != nil {
fmt.Println("Errore nella scrittura del file:", err)
return
}
fmt.Println("File scritto con successo")
16. Pacchetti e moduli
I moduli Go gestiscono le dipendenze e le versioni dei pacchetti:
// Importazione di pacchetti della libreria standard
import (
"fmt"
"math"
"strings"
)
func main() {
fmt.Println(math.Sqrt(16)) // 4
fmt.Println(strings.ToUpper("go")) // GO
}
Per creare il tuo pacchetto, crea una directory con il nome del tuo pacchetto ed esporta le funzioni capitalizzando i loro nomi.
17. Testing
Go ha supporto integrato per il testing:
// Nel file math_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
risultato := add(2, 3)
atteso := 5
if risultato != atteso {
t.Errorf("add(2, 3) = %d; voglio %d", risultato, atteso)
}
}
func add(a, b int) int {
return a + b
}
Esegui i test con: go test
18. Best Practices
- Usa
gofmt
per formattare il tuo codice - Segui le convenzioni di denominazione di Go (camelCase per le variabili, PascalCase per le esportazioni)
- Gestisci gli errori in modo esplicito
- Usa le interfacce per l’astrazione
- Preferisci la composizione all’ereditarietà
- Scrivi test completi
- Usa la libreria standard quando possibile
Questo tutorial copre le caratteristiche essenziali della programmazione in Go. Con la pratica, sarai in grado di costruire applicazioni efficienti e concorrenti utilizzando le potenti caratteristiche di Go.